#include "CommutativityGenerator.h"
#include "SearchEngine.h"

CommutativityGenerator::CommutativityGenerator(int property, int priority)
	: Generator("CommutativityGenerator", property, priority)
{	
}

CommutativityGenerator::~CommutativityGenerator()
{	
}

bool CommutativityGenerator::generate(Query *q, QueryStorage *qs, int sp, int *queryNumber)
{
	query = q;
	queryStorage = qs;
	startpos = sp;
	number = queryNumber;

	opsList.clear();
	query->getOpsList(opsList);

	if (startpos >= opsList.size())// - 2)
	{
		return false;
	}

	generate(opsList.begin() + startpos);
	
	return true;
}

void CommutativityGenerator::generate(std::vector <OperatorNode *> :: iterator begin)
{
	std::vector <OperatorNode *> :: iterator t = begin;	
	
	for (std::vector <OperatorNode *> :: iterator i = t + 1; i != opsList.end(); i++)
	{
		Query *qi = findOperatorNodeinQuery(query, *i);
		Query *qt = findOperatorNodeinQuery(query, *t);

		/*if (((PropertyChecker *)checker)->checkMovable(*t, *i))
		{
			continue;
		}

		if (((PropertyChecker *)checker)->checkDistributivity(*t, *i))
		{
			bool isBranches = false;

			Query *qmin = 0, *qmax = 0;
			Query *p = qi;

			while (p)
			{
				if (p->lists.size() > 1)
				{
					isBranches = true;
				}

				if (p == qt)
				{
					qmin = qi;
					qmax = qt;
					break;
				}

				p = p->parent;
			}

			if (!qmin)
			{
				isBranches = false;
				p = qt;

				while (p)
				{
					if (p->lists.size() > 1)
					{
						isBranches = true;
					}

					if (p == qi)
					{
						qmin = qt;
						qmax = qi;
						break;
					}

					p = p->parent;
				}

				if (!qmin)
				{
					// Operators are in different branches.
					continue;
				}
			}

			if (qmin->lists.size() != qmax->lists.size())
			{
				if (!(qmin->lists.size() && qmax->lists.size()))
				{
					// One of operators has no inputs.
					continue;
				}

				if (qmax->lists.size() < qmin->lists.size())
				{
					Query *q = new Query(*query);

					Query *t = qmin;
					qmin = qmax;
					qmax = t;
					
					Query *parent = qmin->parent;

					if (parent)
					{
						for (std::vector <Query *> :: iterator j = parent->lists.begin(); j != parent->lists.end(); ++j)
						{
							if ((*j) == parent)
							{
								std::replace(j, j, *j, qmin->lists[0]);
								break;
							}
						}
					}
					else
					{
						q = qmin->lists[0];
					}

					qmin->lists[0]->parent = parent;
					qmin->lists.clear();
					qmin->parent = qmax;

					for (std::vector <Query *> :: iterator j = qmax->lists.begin(); j != qmax->lists.end(); ++j)
					{
						Query *insert = new Query(*qmin);

						insert->lists.push_back(*j);
						(*j)->parent = insert;
					
						std::replace(j, j + 1, *j, insert);				
					}

					delete qmin;

					//int d = std::distance(opsList.begin(), i);

					//opsList.clear();
					//query->getOpsList(opsList);

					//t = begin = opsList.begin() + startpos;
					//i = t + 1 + d;
					//i--;

					if (!((PropertyChecker *)checker)->checkDependencies(q))
					{
						delete q;

						// Dependencies check was not accepted.
						continue;
					}

					number++;
					queryStorage->addQuery(new QueryInfo(q, number));		

					// Query accepted and added to storage.
					continue;
				}
				else // qmax->lists.size() > qmin->lists.size()
				{
				}
			}
			else // qmin->lists.size() == qmax->lists.size()
			{
				if (isBranches)
				{
					p = qmin;

					while (p != qmax)
					{
						if (p->lists.size() > 1)
						{
							isBranches = true;
						}

						p = p->parent;
					}

					;
				}

				// Operators will be handled as a commutative.
			}
		}*/
		
		if (((PropertyChecker *)checker)->checkCommutativity(*t, *i))
		{
			if (qi->lists.size() != qt->lists.size())
			{
				// Operators have different number of inputs.
				continue;
			}

			std::swap(*t, *i);
			
			Query *q = createQuery(opsList.begin());

			std::swap(*t, *i);

			if (!((PropertyChecker *)checker)->checkDependencies(q))
			{
				delete q;

				// Dependencies check was not accepted.
				continue;
			}

			queryStorage->addQuery(new QueryInfo(q, ++(*number)));	
		}
	}		
}
